using System.Collections.Concurrent;
using System.Data;
using System.Data.SqlClient;
using Microsoft.Extensions.Logging;

namespace KL.Standard.Repositories.DBContext;

public class TenantDataSourcConfig :Dictionary<string, string>
{
}

/// <summary>
/// 数据库上下文
/// 实现了多租户的概念
/// </summary>
public class MyDbContext
{
    //private static readonly string connString = ConfigurationManager.ConnectionStrings["Eag_DbConnection_Pro"].ConnectionString;
    //private IDbConnection _dbConnection;

    /// <summary>
    /// ID
    /// </summary>
    public readonly string ID = System.Guid.NewGuid().ToString();
    /// <summary>
    /// 日志
    /// </summary>
    private readonly ILogger<MyDbContext> _logger;
    /// <summary>
    /// 多租户数据源配置
    /// </summary>
    private readonly TenantDataSourcConfig _tenantDataSourcConfig;
    /// <summary>
    /// 当前http请求涉及的数据源
    /// </summary>
    private  ConcurrentDictionary<string, IDbConnection> _curDataSources ;
    /// <summary>
    /// 当前http请求的数据事务
    /// </summary>
    private  ConcurrentDictionary<string, IDbTransaction> _curDbTrans ;

    public MyDbContext(TenantDataSourcConfig tenantDataSourcConfig,ILogger<MyDbContext> logger)
    {
        _tenantDataSourcConfig = tenantDataSourcConfig;
        _logger = logger;
        _curDataSources = new ConcurrentDictionary<string, IDbConnection>();
        _curDbTrans= new ConcurrentDictionary<string, IDbTransaction>();
    }
    /// <summary>
    /// 通过租户ID获取数据库连接
    /// </summary>
    /// <param name="tenantId"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    public IDbConnection DbConnection(string tenantId)
    {
        if (_curDataSources.TryGetValue(tenantId, out IDbConnection dbConnection))
        {
            // if (dbConnection.State != ConnectionState.Open) 
            //     dbConnection.Open();
            return dbConnection;
        }
        if (this._tenantDataSourcConfig.TryGetValue(tenantId, out string tenantDbConnectionstr))
        {
            var newDbConnection = new SqlConnection(tenantDbConnectionstr);
            this._curDataSources.TryAdd(tenantId, newDbConnection);
            return newDbConnection;
        }
        throw new Exception($"can not find tenant db connectionstr for tenantId:{tenantId} from config");
        //if (MiniProfiler.Current != null)
        //{
        //    _dbConnection = new StackExchange.Profiling.Data.ProfiledDbConnection((DbConnection)_dbConnection, MiniProfiler.Current);
        //    //https://stackoverflow.com/questions/50581540/dapper-contrib-and-miniprofiler-for-mysql-integration-issues
        //    //SqlMapperExtensions.GetDatabaseType = conn => "MySqlConnection";
        //}
    }





    public IDbTransaction GetDbTransactionByTenantId(string tenantId)
    {
        if (this._curDbTrans.TryGetValue(tenantId, out IDbTransaction dbTransaction))
            return dbTransaction;
        else 
            throw new Exception($"execute [GetDbTransactionByTenantId] can not find db transaction for tenantId:{tenantId}");
    }

    /// <summary>
    /// 是否已被提交
    /// </summary>
    public bool Committed { get; private set; } = true;

    /// <summary>
    /// 开启事务
    /// </summary>
    public void BeginTransaction(string tenantId)
    {
        var dbConnection = DbConnection(tenantId);
        Committed = false;
        bool isClosed = dbConnection.State == ConnectionState.Closed;
        if (isClosed) dbConnection.Open();
        var dbTransaction = dbConnection?.BeginTransaction();
        this._curDbTrans.TryAdd(tenantId, dbTransaction);
    }

    /// <summary>
    /// 事务提交
    /// </summary>
    public void CommitTransaction(string tenanId)
    {
        
        if (this._curDbTrans.TryGetValue(tenanId, out IDbTransaction dbTransaction))
        {
            dbTransaction?.Commit();
            Committed = true;
            this._curDbTrans.TryRemove(tenanId, out _);
            var dbConnection =DbConnection(tenanId);
            if(dbConnection.State == ConnectionState.Open)
                dbConnection.Close(); 
            return;
        }

        throw new Exception($"execute [CommitTransaction] can not find db transaction for tenantId:{tenanId}");
        //Dispose();
    }

    /// <summary>
    /// 事务回滚
    /// </summary>
    public void RollBackTransaction(string tenanId)
    {
        if (this._curDbTrans.TryGetValue(tenanId, out IDbTransaction dbTransaction))
        {
            dbTransaction?.Rollback();
            Committed = true;
            this._curDbTrans.TryRemove(tenanId, out _);
            var dbConnection =DbConnection(tenanId);
            if(dbConnection.State == ConnectionState.Open)
                dbConnection.Close(); 
            return;
        }
        _logger.LogError($"execute [RollBackTransaction] can not find db transaction for tenantId:{tenanId}");
        //throw new Exception($"execute [RollBackTransaction] can not find db transaction for tenantId:{tenanId}");
    }

    /// <summary>
    /// 释放
    /// </summary>
    public void Dispose()
    {
        //DbTransaction?.Dispose();
        foreach (var (_,curDataSource) in _curDataSources)
        {
            if (curDataSource!=null && curDataSource.State  == ConnectionState.Open) curDataSource.Close();
        }
        foreach (var (_,curDbTran) in _curDbTrans)
        {
            curDbTran?.Dispose();
        }
    }
}